import Cesium from '../Ces/Cesium';
import Config from '../Core/Config';
import Util from '../Core/Util';
import WeatherVolumeLibrary from '../Weather/WeatherVolumeLibrary';
import WeLengthwaysShaders from './WeLengthwaysShaders';
import WeUniform from './WeUniform';
import WeDisplayParameter from './WeDisplayParameter';

class WeLengthways {
  // Member variables
  private _weUniform;
  private _positions;
  private _extrudedHeight;
  private _scaleZ;
  private _primitive;
  private _show;
  private _dirty;
  private _display;
  private rectangle;
  private _appearance;
  private _id

  constructor(options) {
    this._id = options.id?? Cesium.createGuid()
    const floatUniform = {
      sumarize: 0.0,
    };

    const vec2Uniform = {
      stPoint: new Cesium.Cartesian2(0.0, 1.0),
      edPoint: new Cesium.Cartesian2(0.0, 1.0)
    };

    const vec4Uniform = {
      localExtent: options.rectangle,
    };

    this._weUniform = new WeUniform({ floatUniform, vec2Uniform, vec4Uniform, name: 'u_WeLengthways' });
    this.extrudedHeight = options.extrudedHeight;
    this.positions = options.positions;
    this.rectangle = options.rectangle;
    this._display = options.display;
    this.scaleZ = options.scaleZ;
    this._primitive = undefined;
    this._show = true;
    this._dirty = true;
  }

  get id() {
    return this._id
  }

  get weUniform(){
    return this._weUniform
  }

  get positions() {
    return this._positions;
  }

  set positions(value) {
    this._positions = value;
    this._dirty = true;
  }

  get dirty() {
    return this._dirty;
  }

  set dirty(value) {
    this._dirty = this._dirty || value;
  }

  get show() {
    return this._show;
  }

  set show(value) {
    this._show = value;
  }

  get scaleZ() {
    return this._scaleZ;
  }

  set scaleZ(value) {
    if (this._scaleZ !== value) {
      this._scaleZ = value;
      this._dirty = true;
    }
  }

  get extrudedHeight() {
    return this._extrudedHeight;
  }

  set extrudedHeight(value) {
    this._extrudedHeight = value;
    this._dirty = true;
  }

  get sumarize() {
    return this._weUniform.Properties.sumarize;
  }

  set sumarize(value) {
    this._weUniform.Properties.sumarize = value;
  }

  showAtLongitude(lon) {
    const rectangle = this.rectangle;

    let longitude;

    if (lon >= rectangle.west && lon <= rectangle.east) {
      longitude = lon;
    } else {
      return;
    }
    this.positions = [longitude, rectangle.south, longitude, rectangle.north];
  }

  showAtLatitude(lat) {
    const rectangle = this.rectangle;

    let latitude;

    if (lat >= rectangle.south && lat <= rectangle.north) {
      latitude = lat;
    } else {
      return;
    }
    this.positions = [rectangle.west, latitude, rectangle.east, latitude];
  }

  rebuild() {
    const granularityFactor = Config.granularityFactor;
    const rectangle = this.rectangle;

    Util.clampLineInRectangle(this.positions, rectangle);

    const p1 = { x: this.positions[0], y: this.positions[1] };
    const p2 = { x: this.positions[2], y: this.positions[3] };

    //@ts-ignore
    const positions = WeatherVolumeLibrary.computeWallPosition(p1, p2, granularityFactor);

    this._weUniform.Properties.stPoint = [p1.x, p1.y];
    this._weUniform.Properties.edPoint = [p2.x, p2.y];

    const minimumHeight = WeDisplayParameter.offsetZ * this.scaleZ;
    const maximumHeight = this.extrudedHeight * this.scaleZ;

    const geometry = Cesium.WallGeometry.createGeometry(
      Cesium.WallGeometry.fromConstantHeights({
        positions,
        minimumHeight,
        maximumHeight,
        granularity: Cesium.Math.RADIANS_PER_DEGREE * granularityFactor,
      }),
    );

    if (!Cesium.defined(this._appearance)) {
      const display = this._display;
      const uniforms = {};
      display.setupUniform(uniforms);
      this._weUniform.setupUniform(uniforms);
      WeDisplayParameter.setupUniform(uniforms);
      const translucent = WeDisplayParameter.Lengthways.alpha < 1.0;

      this._appearance = new Cesium.MaterialAppearance({
        renderState: {
          depthTest: { enabled: true },
          depthMask: true,
          blending: Cesium.BlendingState.ALPHA_BLEND,
          cull: {
            enabled: false,
            face: Cesium.CullFace.FRONT,
          },
        },
        translucent,
      });

      const baseFragmentSource = display.getFragmentShaderSource(true, true, false);

      const fragmentShaderSource = `
        ${this._weUniform.getSource(false)}
        ${baseFragmentSource}
        ${WeDisplayParameter.getSource(false)}
        ${WeLengthwaysShaders}
      `;

      this._appearance.material = new Cesium.Material({
        translucent,
        fabric: {
          uniforms,
          source: fragmentShaderSource,
        },
      });

      display.fixMaterials(this._appearance.material);
    }

    const primitive = new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({ geometry, id: this }),
      asynchronous: false,
      appearance: this._appearance,
    });

    this._primitive = this._primitive && this._primitive.destroy();
    this._primitive = primitive;
  }

  isDestroyed() {
    return false;
  }

  destroy() {
    this._primitive = this._primitive && this._primitive.destroy();
  }

  update(frameState) {
    if (!this._show) {
      return;
    }

    if (this.scaleZ !== WeDisplayParameter.scaleZ) {
      this.scaleZ = WeDisplayParameter.scaleZ;
    }

    if (this._dirty) {
      this._dirty = false;
      this.rebuild();
    }
    if (Cesium.defined(this._primitive)) {
      this._primitive.update(frameState);
    }
  }
}

export default WeLengthways;
